﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;

namespace Hxsoft.AppV1.Module
{
	/// <summary>
	/// 访问控制类。
	/// </summary>
	public class AccessController
	{
		/// <summary>
		/// 通用模块的数据容器。
		/// </summary>
		private GenericModuleDataContext _dc;

		/// <summary>
		/// 构造函数。
		/// </summary>
		/// <param name="dc">初始的数据容器。</param>
		internal AccessController(GenericModuleDataContext dc)
		{
			this._dc = dc;
		}

		/// <summary>
		/// 构造函数。
		/// </summary>
		public AccessController()
			: this(new GenericModuleDataContext())
		{
		}

		/// <summary>
		/// 获取用户所属的角色名称。
		/// </summary>
		/// <typeparam name="OperationObjectType">进行操作的对象类型。</typeparam>
		/// <param name="username">进行操作的用户。</param>
		/// <param name="operationObject">进行操作的对象。</param>
		/// <param name="operationCode">操作代码。</param>
		/// <param name="getRoleMethods">获取附加角色的委托。</param>
		/// <returns>用户所属的角色名称。</returns>
		protected virtual string[] GetSystemRoles<OperationObjectType>(string username, OperationObjectType operationObject, int operationCode, int? siteId, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			List<string> roles = new List<string>();

			foreach (GetSystemRoleDelegate<OperationObjectType> method in getRoleMethods)
			{
				string role = method(username, operationObject, operationCode);
				if (!string.IsNullOrEmpty(role))
					roles.Add(role);
			}

			Guid? userId = Users.GetUserId(username);

			// Everyone
			roles.Add(Roles.EveryoneRoleName);

			// User
			if (userId.HasValue)
				roles.Add(Roles.UserRoleName);

			// 验证是否为管理员
			if (Users.IsAdministrator(username))
				roles.Add(Roles.AdministratorRoleName);

			ICreator creatorEntity = operationObject as ICreator;
			if (creatorEntity != null)
			{
				if (creatorEntity.CreatorId.HasValue && creatorEntity.CreatorId == userId)
					roles.Add(Roles.CreatorRoleName);
			}

			IOwner ownerEntity = operationObject as IOwner;
			if (ownerEntity != null)
			{
				if (ownerEntity.OwnerId.HasValue && ownerEntity.OwnerId == userId)
					roles.Add(Roles.OwnerRoleName);
			}

			return roles.ToArray();
		}

		#region GetUserIsHasCategoryPermission
		/// <summary>
		/// 获取用户是否有指定资源类别下的任何一项资源的指定权限。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="categoryId">指定的资源类别。</param>
		/// <param name="permissionCode">指定的权限码。</param>
		/// <param name="siteId">权限的站点范围。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示用户是否有指定资源类别下的任何一项资源的指定权限。</returns>
		public bool GetUserIsHasCategoryPermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid categoryId, int permissionCode, int? siteId, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			if (!string.IsNullOrEmpty(username))	// 验证用户是否具有权限
			{
				if (this._dc.UserPermissions.GetUserIsHasCategoryPermission(username, categoryId, permissionCode, siteId))
					return true;
			}

			string[] roles = GetSystemRoles<OperationObjectType>(username, operationObject, permissionCode, siteId, getRoleMethods);
			if (roles.Length > 0)	// 验证角色是否具有权限
			{
				if (this._dc.RolePermissions.GetRolesIsHasCategoryPermission(roles, categoryId, permissionCode, siteId))
					return true;
			}

			return false;
		}

		/// <summary>
		/// 获取用户是否有指定资源类别下的任何一项资源的指定权限（不受站点限制）。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="categoryId">指定的资源类别。</param>
		/// <param name="permissionCode">指定的权限码。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示用户是否有指定资源类别下的任何一项资源的指定权限。</returns>
		public bool GetUserIsHasCategoryPermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid categoryId, int permissionCode, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			if (!string.IsNullOrEmpty(username))
			{
				if (this._dc.UserPermissions.GetUserIsHasCategoryPermission(username, categoryId, permissionCode))
					return true;
			}

			string[] roles = GetSystemRoles<OperationObjectType>(username, operationObject, permissionCode, null, getRoleMethods);
			if (roles.Length > 0)	// 验证角色是否具有权限
			{
				if (this._dc.RolePermissions.GetRolesIsHasCategoryPermission(roles, categoryId, permissionCode))
					return true;
			}

			return false;
		}
		#endregion




		#region GetUserIsHasPermission
		/// <summary>
		/// 获取用户是否有指定的权限。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="resourceId">资源标识。</param>
		/// <param name="permissionCode">权限码。</param>
		/// <param name="siteId">权限的站点范围。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示用户是否有指定的权限。</returns>
		public bool GetUserIsHasPermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid resourceId, int permissionCode, int? siteId, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			if (!string.IsNullOrEmpty(username))	// 验证用户是否具有权限
			{
				if (this._dc.UserPermissions.GetUserIsHasPermission(username, resourceId, permissionCode, siteId))
					return true;
			}

			string[] roles = GetSystemRoles<OperationObjectType>(username, operationObject, permissionCode, siteId, getRoleMethods);
			if (roles.Length > 0)	// 验证角色是否具有权限
			{
				if (this._dc.RolePermissions.GetRolesIsHasPermission(roles, resourceId, permissionCode, siteId))
					return true;
			}

			return false;
		}

		/// <summary>
		/// 获取用户是否有指定的权限（不受站点限制）。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="resourceId">资源标识。</param>
		/// <param name="permissionCode">权限码。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示用户是否有指定的权限。</returns>
		public bool GetUserIsHasPermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid resourceId, int permissionCode, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			if (!string.IsNullOrEmpty(username))	// 验证用户是否具有权限
			{
				if (this._dc.UserPermissions.GetUserIsHasPermission(username, resourceId, permissionCode))
					return true;
			}

			string[] roles = GetSystemRoles<OperationObjectType>(username, operationObject, permissionCode, null, getRoleMethods);
			if (roles.Length > 0)	// 验证角色是否具有权限
			{
				if (this._dc.RolePermissions.GetRolesIsHasPermission(roles, resourceId, permissionCode))
					return true;
			}

			return false;
		}
		#endregion




		/// <summary>
		/// 获取指定的用户是否拥有指定站点列表的任意一个的特定权限。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="resourceId">资源标识。</param>
		/// <param name="permissionCode">权限码。</param>
		/// <param name="siteIds">权限的站点范围。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示指定的用户是否拥有指定站点列表的任意一个的特定权限。</returns>
		public bool GetUserIsHasAnySitePermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid resourceId, int permissionCode, int[] siteIds, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			if (!string.IsNullOrEmpty(username))	// 验证用户是否具有权限
			{
				if (this._dc.UserPermissions.GetUserIsHasAnySitePermission(username, resourceId, permissionCode, siteIds))
					return true;
			}

			string[] roles = GetSystemRoles<OperationObjectType>(username, operationObject, permissionCode, null, getRoleMethods);
			if (roles.Length > 0)	// 验证角色是否具有权限
			{
				if (this._dc.RolePermissions.GetRolesIsHasAnySitePermission(roles, resourceId, permissionCode, siteIds))
					return true;
			}

			return false;
		}

		/// <summary>
		/// 获取指定的用户是否拥有指定站点列表的任意一个的特定权限。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="resourceId">资源标识。</param>
		/// <param name="permissionCode">权限码。</param>
		/// <param name="siteIds">权限的站点范围。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示指定的用户是否拥有指定站点列表的任意一个的特定权限。</returns>
		public bool GetUserIsHasAnySitePermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid resourceId, int permissionCode, string siteIds, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			return GetUserIsHasAnySitePermission<OperationObjectType>(username, operationObject, resourceId, permissionCode, Sites.DecompoundOwnedSiteIds(siteIds), getRoleMethods);
		}




		/// <summary>
		/// 获取用户是否有指定的权限。（所有指定的站点）。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="resourceId">资源标识。</param>
		/// <param name="permissionCode">权限码。</param>
		/// <param name="siteIds">权限的站点范围。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示用户是否有指定的权限。</returns>
		public bool GetUserIsHasEverySitePermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid resourceId, int permissionCode, int[] siteIds, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			if (!string.IsNullOrEmpty(username))	// 验证用户是否具有权限
			{
				if (this._dc.UserPermissions.GetUserIsHasEverySitePermission(username, resourceId, permissionCode, siteIds))
					return true;
			}

			string[] roles = GetSystemRoles<OperationObjectType>(username, operationObject, permissionCode, null, getRoleMethods);
			if (roles.Length > 0)	// 验证角色是否具有权限
			{
				if (this._dc.RolePermissions.GetRolesIsHasEverySitePermission(roles, resourceId, permissionCode, siteIds))
					return true;
			}

			return false;
		}

		/// <summary>
		/// 获取用户是否有指定的权限。（所有指定的站点）。
		/// </summary>
		/// <param name="username">用户名。</param>
		/// <param name="operationObjectId">操作对象的标识。（如非具体操作传递null值）</param>
		/// <param name="resourceId">资源标识。</param>
		/// <param name="permissionCode">权限码。</param>
		/// <param name="siteIds">权限的站点范围。</param>
		/// <param name="getRoleMethods">获取系统角色的方法。</param>
		/// <returns>布尔值表示用户是否有指定的权限。</returns>
		public bool GetUserIsHasEverySitePermission<OperationObjectType>(string username, OperationObjectType operationObject, Guid resourceId, int permissionCode, string siteIds, params GetSystemRoleDelegate<OperationObjectType>[] getRoleMethods)
		{
			return GetUserIsHasEverySitePermission<OperationObjectType>(username, operationObject, resourceId, permissionCode, Sites.DecompoundOwnedSiteIds(siteIds), getRoleMethods);
		}
	}

	/// <summary>
	/// 获取系统角色名的委托方法。
	/// </summary>
	/// <typeparam name="OperationObjectType">进行操作的对象类型。</typeparam>
	/// <param name="username">进行操作的用户名。</param>
	/// <param name="operationObject">进行操作的对象。</param>
	/// <param name="operationCode">操作代码。</param>
	/// <returns>系统角色名。</returns>
	public delegate string GetSystemRoleDelegate<OperationObjectType>(string username, OperationObjectType operationObject, int operationCode);




	/// <summary>
	/// 通用模块的数据容器。
	/// </summary>
	public partial class GenericModuleDataContext
	{
		/// <summary>
		/// 访问控制对象。
		/// </summary>
		private AccessController _accessController;

		/// <summary>
		/// 访问控制器对象实例。
		/// </summary>
		internal AccessController AccessController
		{
			get
			{
				if (_accessController == null)
					_accessController = GetAccessController();
				return _accessController;
			}
		}

		/// <summary>
		/// 获取数据访问控制器。
		/// </summary>
		/// <returns>数据访问控制器。</returns>
		/// <remarks>暂时写成常量形式，日后将改为配置形式。</remarks>
		private static AccessController GetAccessController()
		{
			string assemblyName = ApplicationConfiguration.Configuration.AccessControllerAssembly;
			string className = ApplicationConfiguration.Configuration.AccessControllerClassName;
			if(string.IsNullOrEmpty(assemblyName)) assemblyName = "Hxsoft.AppV1.Module";
			if(string.IsNullOrEmpty(className)) className = "Hxsoft.AppV1.Module.ExtAccessController";

			Assembly assembly = Assembly.Load(assemblyName);
			return (AccessController)assembly.CreateInstance(className);
		}
	}
}